package simpledb.buffer;

import simpledb.file.*;

/**
 * Manages the pinning and unpinning of buffers to blocks.
 * 
 * @author Edward Sciore
 *
 */
class BasicBufferMgr {
  private Buffer[] bufferpool;
  private int numAvailable;// 可用的缓冲区个数

  /**
   * Creates a buffer manager having the specified number of buffer slots. This
   * constructor depends on both the {@link FileMgr} and
   * {@link simpledb.log.LogMgr LogMgr} objects that it gets from the class
   * {@link simpledb.server.SimpleDB}. Those objects are created during system
   * initialization. Thus this constructor cannot be called until
   * {@link simpledb.server.SimpleDB#initFileAndLogMgr(String)} or is called
   * first.<br>
   * 创建具有指定数量的缓冲区管理器 的缓冲槽。 此构造函数取决于{@link FileMgr}和 {@link simpledb.log.LogMgr
   * LogMgr}对象 它从类{@link simpledb.server.SimpleDB}中获取 。 这些对象在系统初始化期间创建。
   * 因此这个构造函数不能被调用或者先调用
   * {@link simpledb.server.SimpleDB#initFileAndLogMgr(String)}。
   * 
   * @param numbuffs the number of buffer slots to allocate
   */
  BasicBufferMgr(int numbuffs) {
    bufferpool = new Buffer[numbuffs];
    numAvailable = numbuffs;
    for (int i = 0; i < numbuffs; i++)
      bufferpool[i] = new Buffer();
  }

  /**
   * Flushes the dirty buffers modified by the specified transaction.<br>
   * 刷新由指定事务修改的脏缓冲区
   * 
   * @param txnum the transaction's id number 事务的ID号
   */
  synchronized void flushAll(int txnum) {
    for (Buffer buff : bufferpool)
      if (buff.isModifiedBy(txnum))
        buff.flush();
  }

  /**
   * Pins a buffer to the specified block. If there is already a buffer assigned
   * to that block then that buffer is used; otherwise, an unpinned buffer from
   * the pool is chosen. Returns a null value if there are no available buffers.
   * <br>
   * 将缓冲区定位到指定的块。 如果已经有一个缓冲区分配给该块，则使用该缓冲区; 否则，选择来自池的未固定缓冲器。 如果没有可用的缓冲区，则返回空值。
   * 
   * @param blk a reference to a disk block 引用磁盘块
   * @return the pinned buffer 固定缓冲区
   */
  synchronized Buffer pin(Block blk) {
    // 块是否被使用Buffer使用
    Buffer buff = findExistingBuffer(blk);
    if (buff == null) {
      // 选择一个没有被使用的缓冲区
      buff = chooseUnpinnedBuffer();
      if (buff == null)
        return null;
      buff.assignToBlock(blk);
    }
    if (!buff.isPinned())
      numAvailable--;
    buff.pin();
    return buff;
  }

  /**
   * Allocates a new block in the specified file, and pins a buffer to it.
   * Returns null (without allocating the block) if there are no available
   * buffers.<br>
   * 在指定的文件中分配一个新的块，并将缓冲区固定到它。 如果没有可用的缓冲区，则返回null（不分配块）。
   * 
   * @param filename the name of the file
   * @param fmtr a pageformatter object, used to format the new block
   *          一个pageformatter对象，用于格式化新块
   * @return the pinned buffer
   */
  synchronized Buffer pinNew(String filename, PageFormatter fmtr) {
    Buffer buff = chooseUnpinnedBuffer();
    if (buff == null)
      return null;
    buff.assignToNew(filename, fmtr);
    numAvailable--;
    buff.pin();
    return buff;
  }

  /**
   * Unpins the specified buffer. 取消指定的缓冲区的Unpins。
   * 
   * @param buff the buffer to be unpinned
   */
  synchronized void unpin(Buffer buff) {
    buff.unpin();
    if (!buff.isPinned())
      numAvailable++;
  }

  /**
   * Returns the number of available (i.e. unpinned) buffers.<br>
   * 返回可用（即未固定）缓冲区的数量。
   * 
   * @return the number of available buffers
   */
  int available() {
    return numAvailable;
  }

  /**
   * 输入一个Block兑现的引用，遍历缓冲池的缓冲片，看是否有Buffer已经分配给该Block
   * 
   * @param blk
   * @return
   */
  private Buffer findExistingBuffer(Block blk) {
    for (Buffer buff : bufferpool) {
      Block b = buff.block();
      if (b != null && b.equals(blk))
        return buff;
    }
    return null;
  }

  /**
   * 遍历整个缓冲池，查看是有有未使用的缓冲片。
   * 
   * @return
   */
  private Buffer chooseUnpinnedBuffer() {
    for (Buffer buff : bufferpool)
      if (!buff.isPinned())
        return buff;
    return null;
  }
}
